package org.atlantis.grosbeak.depot.loader;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.atlantis.grosbeak.depot.Boult;
import org.atlantis.grosbeak.depot.DepotException;
import org.atlantis.grosbeak.depot.DepotLoader;
import org.atlantis.grosbeak.depot.desc.ObjectDesc;

/**
 * 缓存配置加载方式。其实此类是一个代理类，它并不知道如何从配置文件中加载对象。
 * 它只是代理其它加载方式（如JsonLoader、MapLoader）。它在其它配置加载方式加载对象 后给单态对象加上一个缓存。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
public class CachedLoader implements DepotLoader {

	private DepotLoader loader;

	private Map<String, ObjectDesc> map;

	private Map<Class<?>, ArrayList<String>> boultIds;

	private Map<Class<?>, Map<String, Object>> boultCallbacks;

	private CachedLoader(DepotLoader loader, Boult... boults) {
		this.loader = loader;
		this.map = new HashMap<String, ObjectDesc>();
		this.boultIds = new HashMap<Class<?>, ArrayList<String>>();
		this.boultCallbacks = new HashMap<Class<?>, Map<String, Object>>();
		if (boults.length > 0) { // 如果不需要筛选器，那就不去遍历容器了
			for (String id : loader.getIds()) {
				Class<?> type = loader.load(id).getType();
				for (Boult boult : boults) {
					if (boult.filter(type)) {
						ArrayList<String> arrList = boultIds.get(boult
								.getClass());
						Map<String, Object> map = boultCallbacks.get(boult
								.getClass());
						if (arrList == null)
							arrList = new ArrayList<String>();
						if (map == null)
							map = new HashMap<String, Object>();
						arrList.add(id);
						boultIds.put(boult.getClass(), arrList);

						map.put(id, boult.callback(type));
						boultCallbacks.put(boult.getClass(), map);
					}
				}
			}
		}
	}

	/**
	 * 清理缓存。
	 */
	public void clear() {
		map.clear();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.atlantis.grosbeak.depot.DepotLoader#getIds()
	 */
	public String[] getIds() {
		return loader.getIds();
	}

	/**
	 * 得到经过筛选后的容器中所有对象的ID。
	 * 
	 * @return 经过筛选后容器中所有对象的ID.
	 */
	public String[] getFilterIds(Class<? extends Boult> boultType) {
		ArrayList<String> idList = boultIds.get(boultType);
		if (idList != null)
			return idList.toArray(new String[idList.size()]);
		return new String[] {};
	}

	/**
	 * 获得筛选器的回调方法所产生的结果。
	 * 
	 * @param boultType
	 *            筛选器。
	 * @return Map<String, Object>中String是容器中对象的ID，Object是筛选器的回调方法所产生的结果。
	 */
	public Map<String, Object> getBoultCallbacks(
			Class<? extends Boult> boultType) {
		Map<String, Object> callbacks = boultCallbacks.get(boultType);
		if (callbacks != null)
			return callbacks;
		return new HashMap<String, Object>();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.atlantis.grosbeak.depot.DepotLoader#has(java.lang.String)
	 */
	public boolean has(String id) {
		return loader.has(id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.atlantis.grosbeak.depot.DepotLoader#load(java.lang.String)
	 */
	public ObjectDesc load(String id) throws DepotException {
		ObjectDesc od = map.get(id);
		if (od == null) {
			od = loader.load(id);
			if (od.isSingleton() && od.getType() != null) {
				map.put(id, od);
			}
		}
		return od;
	}

	/**
	 * 实例化一个CachedLoader对象。
	 * 
	 * @param loader
	 *            其它真正的配置加载方式。
	 * @return CachedLoader对象。
	 */
	public static CachedLoader instance(DepotLoader loader, Boult... boults) {
		return new CachedLoader(loader, boults);
	}
}
